import sys
from PyQt5.QtWidgets import * #导入PyQt相关模块
from PyQt5.QtGui import *
from PyQt5.QtCore import *
#from pyqtgraph.graphicsItems import *
import pyqtgraph as pg
import math
from ui_pgr import * #导入窗口
import numpy as np
from PIL import Image #pillow

global dir_str
global scene,scene_buf,scene_buf2,vb
global item
global pic
global grayimg#Gray picture
global imgarray

#class Mywindow(QMainWindow,Ui_Form):   #使用这个的话界面布局会乱
class roiwidge(QWidget,Ui_Form):#界面布局自动缩放
    def __init__(self,parent=None):
        super(roiwidge,self).__init__(parent)
        self.setupUi(self)
        self.setWindowTitle("ROI")

        self.graphicsView.mouseReleaseEvent = self.gpmouseReleaseEvent
        self.graphicsView.mousePressEvent = self.gpmousePressEvent
        self.graphicsView.mouseDoubleClickEvent = self.gpmouseDoubleClickEvent
        self.loadButton.clicked.connect(self.load_click)
        self.loadButton.setText("Select File")
        self.ClearROIButton.clicked.connect(self.ClearAllROI)#清除界面所有ROI
        #self.SetROIcolor.clicked.connect(self.SetRoiColorclick)
        
        self.RectROI.setChecked(True)
        self.lastPoint = QPoint()
        self.endPoint = QPoint()
        self.PenColor = QColor("red").name()
        self.points = list()#仅多边形使用
        self.name = list()
        self.rois = list()
        self.MultiRoiDone = False
        self.multiroi_count = 0
    def load_click(self):#选择图像文件路径
        global dir_str
        global scene
        global scene_buf
        global scene_buf2
        global vb
        global item
        global pic
        global grayimg#Gray picture
        global imgarray
        #self.listWidget.clear()#清空界面缓存
        self.tableWidget.clear()
        self.tableWidget.setHorizontalHeaderLabels(['ROI','Averange'])#设置表头文字
        # logging.info(self.tableWidget.rowCount())
        # self.tableWidget.setRowCount(1)
        # self.tableWidget.setItem(0,0,QTableWidgetItem("测试1"))
        # self.tableWidget.setItem(0,1,QTableWidgetItem("测试2"))
        #self.filelocation.setText("测试界面")
        #self指向自身，"Open File"为文件名，"./"为当前路径，最后为文件类型筛选器
        fname,ftype = QFileDialog.getOpenFileName(self, "Open File", "C:", "tiff(*.tiff);;png(*.png);;jpg(*.jpg)")#如果添加一个内容则需要加两个分号
        # 该方法返回一个tuple,里面有两个内容，第一个是路径， 第二个是要打开文件的类型，所以用两个变量去接受
        # 如果用户主动关闭文件对话框，则返回值为空
        if fname[0]:  # 判断路径非空
            dir_str = fname
            buf = fname
            buf = buf.replace("\\", "/")
            self.setWindowTitle("ROI--" + buf)
            #show picture
            #pic = QtGui.QPixmap(dir_str)
            grayimg = Image.open(dir_str).convert('L')
            #grayimg.show()
            imgarray = np.asarray(grayimg)
            size_x = len(imgarray[0])
            size_y = len(imgarray)
            qimg = QImage(dir_str)
            pic = QPixmap.fromImage(qimg)
            #img.show()
            item = QGraphicsPixmapItem(pic)
            scene = QGraphicsScene(0,0,size_x,size_y)
            scene_buf2 = 0
            scene_buf = pg.ImageItem()
            scene_buf.setImage(imgarray)
            vb = pg.ViewBox()
            vb.addItem(scene_buf)
            #scene_buf = QGraphicsScene(0,0,size_x,size_y)
            scene.addItem(item)
            #scene_buf.addItem(item)
            self.graphicsView.setScene(scene)
    #鼠标滚轮        
    def wheelEvent(self, event):
        self.scaleView(math.pow(2.0,event.angleDelta().y() / 240.0))
        pass
    #图片缩放
    def scaleView(self,scaleFactor):
        factor = self.graphicsView.transform().scale(scaleFactor,scaleFactor).mapRect(QRectF(0,0,1,1)).width()
        if (factor < 0.07 or factor > 100 ):
            return
        self.graphicsView.scale(scaleFactor,scaleFactor)
    #鼠标事件
    def gpmousePressEvent(self,event):
        if event.buttons() == QtCore.Qt.LeftButton:
            self.MultiRoiDone = False
            point = self.graphicsView.mapToScene(event.pos())
            if self.MultiRectROI.isChecked() == True:
                self.points.append(point)
                self.test(points=self.points)
            else:
                self.lastPoint = point
        elif event.buttons() == QtCore.Qt.RightButton:
            self.MultiRoiDone = True
            self.test(points=self.points)
            #self.RectROI.setChecked(True)
        event.accept()
    def gpmouseReleaseEvent(self,event):
        #if event.buttons() == QtCore.Qt.LeftButton:
        logging.info("release left")
        point = self.graphicsView.mapToScene(event.pos())
        self.endPoint = point
        (x,y,w,h) = self.caculat_Rec(self.lastPoint,self.endPoint)
        # try:
        #     self.test((x,y),(w,h))
        # except Exception as e:
        #     logging.info("捕获异常",e)    
        # finally:
        #     pass
        self.test((x,y),(w,h))
    def gpmouseDoubleClickEvent(self,event):
        pass
    #计算矩形ROI的原点和长宽
    def caculat_Rec(self,pos1,pos2):
        x1 = pos1.x()
        x2 = pos2.x()
        y1 = pos1.y()
        y2 = pos2.y()
        logging.info(x1,x2,y1,y2)
        if x1 >= x2:
            x = x2
        else :
            x = x1
        if y1 >= y2:
            y = y2
        else :
            y = y1
        h = abs(y1-y2)
        w = abs(x1-x2)
        return x,y,w,h
    #ROI
    def test(self,pos=(0,0),size=(0,0),points=list()):
        global frame
        global scene
        global scene_buf
        global scene_buf2
        global vb
        if self.RectROI.isChecked() == True:
            frame = pg.RectROI(pos,size)
            self.name.append("roi{}".format(len(self.name)+1))
            frame.addTranslateHandle((0,0),name=self.name[-1],index=len(self.name)-1)
            scene.addItem(frame)
            #logging.info(pos,size)
            self.rois.append(frame)
            buf = imgarray[int(pos[1]):int(pos[1]+size[1]),int(pos[0]):int(pos[0]+size[0])]
            self.Caculat_Info(buf)
        elif self.CircleROI.isChecked() == True:
            #frame = pg.CircleROI(pos,size)  #EllipseROI
            frame = pg.EllipseROI(pos,size)
            self.name.append("roi{}".format(len(self.name)+1))
            frame.addTranslateHandle((0,0),name=self.name[-1],index=len(self.name)-1)
            scene.addItem(frame)
            self.rois.append(frame)

            vb.addItem(frame)
            buf,mask = frame.getArrayRegion(imgarray,scene_buf,axes=(1, 0))#此处修改了库函数2022年4月29日
            vb.removeItem(frame)
            scene.addItem(frame)
            self.Caculat_CircleInfo(buf,mask)
        elif self.MultiRectROI.isChecked() == True:
            if self.MultiRoiDone:
                pos = self.points[0]
                frame = pg.PolyLineROI([])
                #frame = pg.PolyLineROI(points).setPoints(points,closed=True)
                frame.setPoints(points,closed=True)
                self.name.append("roi{}".format(len(self.name)+1))
                frame.addTranslateHandle((0,0),name=self.name[-1],index=len(self.name)-1)
                self.rois.append(frame)
                #获取ROI区域
                mask,buf = self.Get_MultiROIMask(imgarray,points)
                # buf1 = np.transpose(buf)
                # mask1 = np.transpose(mask)
                # np.savetxt("buf",buf,fmt='%.1f')
                # np.savetxt("mask1",mask,fmt='%.1f')
                self.multiroi_count = 0
                scene.addItem(frame)
                mask1 = np.transpose(mask)
                self.Caculat_CircleInfo(buf,mask1)
                self.points.clear()
            else:
                if self.multiroi_count == 0 :
                    scene_buf2 = len(scene.items())                       
                self.multiroi_count = self.multiroi_count + 1
                frame = pg.PolyLineROI(points)
                scene.addItem(frame)
    #Set ROI color
    # def SetRoiColorclick(self):
    #     self.PenColor = QColorDialog.getColor().name()
    def Caculat_Info(self,piclist):
        sum = 0
        count = 0
        avg = 0
        for xlist in piclist:
            for ylist in xlist:
                sum = sum + ylist
                count = count + 1
        avg = sum /count
        self.Add_tableWidget(avg)
        #self.listWidget.addItem(self.name[-1]+"average is:"+str(avg))
        #self.tableWidget.items().append(avg)
    def ClearAllROI(self):
        global scene
       # self.listWidget.clear()#清空界面缓存
        self.tableWidget.clear()
        self.tableWidget.setHorizontalHeaderLabels(['ROI','Averange'])#设置表头文字
        self.tableWidget.setRowCount(0)
        self.rois.clear()
        self.points.clear()
        self.name.clear()
        self.rois.clear()
        #del scene.items()[0:-1]
        for i in range(len(scene.items())-1):
            scene.removeItem(scene.items()[0])
        logging.info(scene.items())
    def Caculat_CircleInfo(self,data,mask):
        mask1 = np.transpose(mask)
        sum = 0
        count = 0
        avg = 0
        for i in range(len(mask1)-1):
            for j in range(len(mask1[0])-1):
                if mask1[i,j]:
                    sum = sum + data[i,j]
                    count = count + 1
        avg = sum / count
        self.Add_tableWidget(avg)
        #self.listWidget.addItem(self.name[-1]+"average is:"+str(avg))
    #判断点在多边形范围内，其中pt为点，poly为多边形区域
    #版权声明：本文为CSDN博主「可可爱爱的小肥肥」的原创文章，遵循CC 4.0 BY-SA版权协议，转载请附上原文出处链接及本声明。
    #原文链接：https://blog.csdn.net/ftmsz12345678/article/details/115375461
    def isInsidePolygon(self,pt, poly):
        c = False
        i = -1
        l = len(poly)
        j = l - 1
        while i < l - 1:
            i += 1
            if((poly[i][0]<=pt[0] and pt[0] < poly[j][0])or(
                poly[j][0]<=pt[0] and pt[0]<poly[i][0] )):
                if(pt[1]<(poly[j][1]-poly[i][1]) * (pt[0]-poly[i][0])/(
                    poly[j][0]-poly[i][0])+poly[i][1]):
                    c = not c
            j=i
        return c
    def Get_MultiROIMask(self,picbuf,points):#picbuf传入整幅图像二维数组，points传入原始坐标列表
        max_x = 0
        max_y = 0
        picbuf1 = np.transpose(picbuf)
        min_x = len(picbuf1)#行数量
        min_y = len(picbuf1[0])#列数量
        point_list = np.zeros((len(points),2))
        for i in range(len(points)):
            point_list[i][0] = points[i].x()
            point_list[i][1] = points[i].y()
            if points[i].x() >= max_x:
                max_x = points[i].x()
            if points[i].y() >= max_y:
                max_y = points[i].y()
            if points[i].x() <= min_x:
                min_x = points[i].x()
            if points[i].y() <= min_y:
                min_y = points[i].y()
        #截取多边形所在矩形区域像素值并进行坐标平移
        minx_t = int(min_x)
        miny_t = int(min_y)
        maxx_t = int(max_x)
        maxy_t = int(max_y)
        picbuf1 = picbuf1[minx_t:(maxx_t+1),miny_t:(maxy_t+1)]
        for i in range(len(points)):
            point_list[i][0] = point_list[i][0] - minx_t
            point_list[i][1] = point_list[i][1] - miny_t
        mask_buf = np.zeros(((maxx_t-minx_t),(maxy_t-miny_t)))#根据多边形所在矩形区域大小创建全0mask,减少计算量
        for x in range(len(picbuf1)):
            for y in range(len(picbuf1[0])):
                if self.isInsidePolygon((x,y),point_list):
                    mask_buf[x][y] = 1
        return (mask_buf,picbuf1)
    def Add_tableWidget(self,avg):
        a = self.tableWidget.rowCount()
        self.tableWidget.setRowCount(a+1)
        logging.info(self.name[-1])
        logging.info('%.2f' % avg)
        logging.info(self.tableWidget.rowCount())
        self.tableWidget.setItem(a,0,QTableWidgetItem(str(self.name[-1])))
        self.tableWidget.setItem(a,1,QTableWidgetItem(str('%.2f' % avg)))
if __name__=='__main__':
    app = QApplication(sys.argv)
    myWin = roiwidge()
    myWin.show()
    sys.exit(app.exec_())